from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.db.postgres import get_session
from app.db.mongo import db
from app.models import EvaluationReport, EvaluationReportActivity, Activity, Event
from bson import ObjectId
import datetime
import uuid
from app.services.llm import call_llm
import logging
from app.services.reporting import export_version_to_docx
from app.services.reporting import export_version_to_pdf
from app.routers.template_outline import get_outline


router = APIRouter(prefix="/report-content", tags=["report-content"])


async def _ensure_report_doc(session: AsyncSession, version: EvaluationReport, first_section_no: str | None = None):
    if version.mongo_report_id:
        doc = await db.reports.find_one({"_id": ObjectId(version.mongo_report_id)})
        if doc:
            return doc
    acts = await session.execute(select(EvaluationReportActivity.activity_id).where(EvaluationReportActivity.report_id == version.id))
    activity_ids = [str(x) for x in acts.scalars().all()]
    if not activity_ids:
        activity_ids = ["__placeholder__"]
    sections: list[dict] = []
    if first_section_no:
        sections.append({
            "section_no": first_section_no,
            "section_name": "",
            "level": 1,
            "status": "draft",
            "content": {"raw_text": "", "candidates": []},
            "audit": {"created_at": datetime.datetime.utcnow(), "created_by": "system"},
        })
    else:
        sections.append({
            "section_no": "__placeholder__",
            "section_name": "",
            "level": 1,
            "status": "draft",
            "content": {"raw_text": "", "candidates": []},
            "audit": {"created_at": datetime.datetime.utcnow(), "created_by": "system"},
        })
    doc = {
        "activity_ids": activity_ids,
        "title": version.title,
        "template_version_code": version.template_version_code,
        "sections": sections,
        "status": "draft",
        "audit": {"created_at": datetime.datetime.utcnow(), "created_by": "system"},
    }
    r = await db.reports.insert_one(doc)
    version.mongo_report_id = str(r.inserted_id)
    await session.flush()
    await session.commit()
    return await db.reports.find_one({"_id": r.inserted_id})


@router.post("/{version_id}/sections/{section_no}/generate")
async def generate(version_id: str, section_no: str, payload: dict, session: AsyncSession = Depends(get_session)):
    v = await session.get(EvaluationReport, version_id)
    if not v:
        raise HTTPException(status_code=404, detail="version_not_found")
    doc = await _ensure_report_doc(session, v, section_no)
    prompt = str(payload.get('prompt') or '')
    conv_id = str(payload.get('conversation_id') or '')
    act_obj = None
    act = payload.get('activity') or {}
    act_id = act.get('id')
    if act_id:
        act_obj = await session.get(Activity, str(act_id))
    event_objs = []
    for c in (payload.get('context') or []):
        if (c or {}).get('type') == 'event' and c.get('id'):
            ev = await session.get(Event, str(c.get('id')))
            if ev:
                event_objs.append(ev)
    sys_lines = []
    if act_obj:
        sys_lines.append(f"活动名称：{act_obj.name}")
        sys_lines.append(f"活动地点：{act_obj.location_admin} {act_obj.location_detail}")
        sys_lines.append(f"活动时间：{act_obj.time_start} 至 {act_obj.time_end}")
    for ev in event_objs:
        sys_lines.append(f"事件：{ev.name}，时间：{ev.occurred_at}")
    system_content = "\n".join(sys_lines) if sys_lines else ""
    messages = []
    if system_content:
        messages.append({"role": "system", "content": system_content})
    messages.append({"role": "user", "content": prompt})
    logging.info("[Generate] version_id=%s section_no=%s conversation_id=%s model_id=%s", version_id, section_no, str(payload.get('model_id') or ''), conv_id)
    logging.info("[Generate] messages=%s", messages)
    try:
        res = await call_llm(session, str(payload.get('model_id') or ''), messages)
        gen_text = res.get('text') or ''
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    candidate = {
        "candidate_id": str(uuid.uuid4()),
        "run_id": str(uuid.uuid4()),
        "text": gen_text,
        "metrics": {"tokens_in": len(prompt), "tokens_out": len(gen_text)},
        "created_at": datetime.datetime.utcnow(),
        "selected": False,
    }
    section = next((s for s in doc.get("sections", []) if s.get("section_no") == section_no), None)
    if not section:
        section = {
            "section_no": section_no,
            "section_name": "",
            "level": 1,
            "status": "draft",
            "content": {"raw_text": "", "candidates": []},
            "audit": {"created_at": datetime.datetime.utcnow(), "created_by": "system"},
        }
        doc["sections"].append(section)
    content = section.get("content", {})
    arr = content.get("candidates", [])
    arr.append(candidate)
    content["candidates"] = arr
    section["content"] = content
    await db.reports.update_one({"_id": doc["_id"]}, {"$set": {"sections": doc["sections"]}})
    run_doc = {
        "report_id": str(v.id),
        "section_no": section_no,
        "conversation_id": conv_id or None,
        "input_snapshot": {},
        "prompt": prompt,
        "llm_params": {"model_id": str(payload.get('model_id') or '')},
        "messages": messages,
        "output_text": gen_text,
        "metrics": {"tokens_in": len(prompt), "tokens_out": len(gen_text)},
        "status": "success",
        "created_at": datetime.datetime.utcnow(),
        "created_by": "system",
    }
    await db.section_generation_runs.insert_one(run_doc)
    user_msg_id = None
    assistant_msg_id = None
    if conv_id:
        user_msg_id = str(uuid.uuid4())
        assistant_msg_id = str(uuid.uuid4())
        await db.section_conversations.update_one(
            {"report_id": str(v.id), "section_no": section_no, "conversation_id": conv_id},
            {"$push": {"messages": {"msg_id": user_msg_id, "role": "user", "content": prompt, "created_at": datetime.datetime.utcnow()}}},
        )
        await db.section_conversations.update_one(
            {"report_id": str(v.id), "section_no": section_no, "conversation_id": conv_id},
            {"$push": {"messages": {"msg_id": assistant_msg_id, "role": "assistant", "content": gen_text, "candidate_id": candidate["candidate_id"], "created_at": datetime.datetime.utcnow()}}},
        )
    return {"candidates": [candidate], "message_ids": {"user_msg_id": user_msg_id, "assistant_msg_id": assistant_msg_id}}


@router.post("/{version_id}/sections/{section_no}/adopt")
async def adopt(version_id: str, section_no: str, payload: dict, session: AsyncSession = Depends(get_session)):
    v = await session.get(EvaluationReport, version_id)
    if not v:
        raise HTTPException(status_code=404, detail="version_not_found")
    if not v.mongo_report_id:
        raise HTTPException(status_code=400, detail="no_report_doc")
    cid = payload.get("candidate_id")
    doc = await db.reports.find_one({"_id": ObjectId(v.mongo_report_id)})
    if not doc:
        raise HTTPException(status_code=404, detail="doc_not_found")
    for s in doc.get("sections", []):
        if s.get("section_no") == section_no:
            for c in (s.get("content", {}).get("candidates", []) or []):
                c["selected"] = (c.get("candidate_id") == cid)
                if c.get("selected"):
                    s.setdefault("content", {})["final_text"] = c.get("text")
                    s["content"]["selected_candidate_id"] = cid
            break
    await db.reports.update_one({"_id": ObjectId(v.mongo_report_id)}, {"$set": {"sections": doc["sections"]}})
    return {"ok": True}


@router.get("/{version_id}/preview")
async def preview(version_id: str, session: AsyncSession = Depends(get_session)):
    v = await session.get(EvaluationReport, version_id)
    if not v or not v.mongo_report_id:
        return {"html": ""}
    doc = await db.reports.find_one({"_id": ObjectId(v.mongo_report_id)})
    parts = []
    for s in doc.get("sections", []):
        t = s.get("content", {}).get("final_text")
        if t:
            parts.append(f"<h3>{s.get('section_no','')}</h3><div>{t}</div>")
    return {"html": "".join(parts)}


@router.get("/{version_id}/sections/contents")
async def section_contents(version_id: str, session: AsyncSession = Depends(get_session)):
    v = await session.get(EvaluationReport, version_id)
    if not v or not v.mongo_report_id:
        return {"items": []}
    doc = await db.reports.find_one({"_id": ObjectId(v.mongo_report_id)})
    items = []
    for s in doc.get("sections", []) or []:
        items.append({
            "section_no": s.get("section_no"),
            "final_text": (s.get("content", {}) or {}).get("final_text")
        })
    return {"items": items}


@router.post("/{version_id}/export")
async def export(version_id: str, payload: dict, session: AsyncSession = Depends(get_session)):
    v = await session.get(EvaluationReport, version_id)
    if not v:
        raise HTTPException(status_code=404, detail="version_not_found")
    if not v.mongo_report_id:
        raise HTTPException(status_code=400, detail="no_report_doc")
    doc = await db.reports.find_one({"_id": ObjectId(v.mongo_report_id)})
    if not doc:
        raise HTTPException(status_code=404, detail="doc_not_found")
    # 始终以模板大纲为基础生成章节顺序与标题
    try:
        outline = (await get_outline(v.template_version_code)).get("outline", [])
    except Exception:
        outline = []
    # 建立 sections 按编号的映射，取已采纳最终文本
    by_no = {str(s.get("section_no") or ""): s for s in (doc.get("sections", []) or [])}
    items = []
    for o in outline or []:
        no = str(o.get("section_no") or "")
        s = by_no.get(no) or {}
        t = ((s.get("content", {}) or {}).get("final_text")) or ""
        items.append({
            "section_no": no,
            "section_name": o.get("section_name") or "",
            "level": int(o.get("level") or 1),
            "text": t,
        })
    # 若模板大纲为空，退化为文档已有sections顺序
    if not items:
        for s in doc.get("sections", []) or []:
            t = (s.get("content", {}) or {}).get("final_text") or ""
            items.append({
                "section_no": s.get("section_no"),
                "section_name": s.get("section_name") or "",
                "level": int(s.get("level") or 1),
                "text": t,
            })
    export_type = str(payload.get("type") or "docx").lower()
    if export_type == "pdf":
        res = await export_version_to_pdf(session, db, v, items)
        return {"file_url": res.get("file_url"), "rel_path": res.get("rel_path"), "type": "pdf"}
    else:
        res = await export_version_to_docx(session, db, v, items)
        return {"file_url": res.get("file_url"), "rel_path": res.get("rel_path"), "type": "docx"}
